package cmd

import (
	"context"
	"fmt"
	"gitee/kubehark/kubehark-gateway/pkg/middlewares"
	"gitee/kubehark/kubehark-gateway/pkg/rbac"
	"gitee/kubehark/kubehark-gateway/pkg/response"
	"gitee/kubehark/kubehark-operator/client/clientset/versioned"
	"github.com/gin-gonic/gin"
	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	restclient "k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/kubectl/pkg/proxy"
	"net/http"
	"sync"
	"time"
)

var CmdFlag *CmdServer

type CmdServer struct {
	Port       string
	CertFile   string
	KeyFile    string
	ClientId   string
	Issuer     string
	Secret     string
	Kubeconfig string
	RbacUrl    string
}

func (this *CmdServer) SetFlag(command *cobra.Command) {
	command.Flags().StringVar(&this.Port, "port", "", "port")
	command.Flags().StringVar(&this.CertFile, "certfile", "", "certfile")
	command.Flags().StringVar(&this.KeyFile, "keyfile", "", "keyfile")
	command.Flags().StringVar(&this.ClientId, "clientId", "", "ClientId")
	command.Flags().StringVar(&this.Issuer, "issuer", "", "Issuer")
	command.Flags().StringVar(&this.Secret, "secret", "", "Secret")
	command.Flags().StringVar(&this.Kubeconfig, "kubeconfig", "", "Kubeconfig")
	command.Flags().StringVar(&this.RbacUrl, "rbacurl", "", "rbacurl")
}

func NewCmdServer() *cobra.Command {
	str := CmdServer{}
	CmdFlag = &str
	var Cmd = &cobra.Command{
		Use:   "server",
		Short: "server",
		Long:  `kubehark server端`,
		Run: func(cmd *cobra.Command, args []string) {
			config, err := clientcmd.BuildConfigFromFlags("", str.Kubeconfig)
			if err != nil {
				return
			}
			TheclientSet = versioned.NewForConfigOrDie(config)

			router := gin.New()
			router.Use(middlewares.Oidc(str.ClientId, str.Issuer, str.Secret))
			router.Any(
				"cluster/:clusterID/*proxyPath",
				clusterProxy,
			)
			router.GET("/switch/:clusterID", switchCluster)
			router.RunTLS(fmt.Sprintf(":%s", str.Port), str.CertFile, str.KeyFile)
		},
	}
	str.SetFlag(Cmd)
	return Cmd
}

func switchCluster(c *gin.Context) {
	user, _ := c.Get("user")
	clusterID := c.Param("clusterID")

	data, err := TheclientSet.KubeharkV1().Credentials("default").Get(context.TODO(), clusterID, metav1.GetOptions{})
	if err != nil {
		response.GInFailed(c, err.Error())
		return
	}
	fmt.Println(user, clusterID)
	fmt.Println(data.Labels)
	fmt.Println(c.Request.Method)
	// 判断是否有权限
	input := rbac.Input{
		User:   user.(string),
		Labels: data.Labels,
		Method: c.Request.Method,
	}
	err, res := rbac.GetOpa(CmdFlag.RbacUrl, input)
	if err != nil {
		response.GInFailed(c, err.Error())
		return
	}
	if len(res.Data) != 0 && res.Data[0].Bindings.Allow {
		response.GinSuccess(c, fmt.Sprintf("登录 %s 成功", clusterID))
		return
	} else {
		response.GInFailed(c, fmt.Sprintf("登录 %s 失败  用户名%s没有权限操作该集群", clusterID, user.(string)))
	}

}

// clusterProxy comment lint rebel
func clusterProxy(c *gin.Context) {
	client := TheclientSet
	clusterID := c.Param("clusterID")
	t, err := getToken(clusterID, client)
	if err != nil {
		c.JSON(http.StatusUnauthorized, metav1.Status{
			Code:    http.StatusUnauthorized,
			Message: err.Error(),
		})
		return
	}
	proxyPath := c.Param("proxyPath")
	t.Do(c.Writer, c.Request, proxyPath)
	return
}

var (
	tokenMap     sync.Map
	TheclientSet *versioned.Clientset
)

// getToken comment lint rebel
func getToken(clusterID string, client *versioned.Clientset) (t *Token, err error) {
	var (
		cacheToken interface{}
		ok         bool
	)
	cacheToken, ok = tokenMap.Load(clusterID)
	if !ok {
		data, err := client.KubeharkV1().Credentials("default").Get(context.TODO(), clusterID, metav1.GetOptions{})
		if err != nil {
			return nil, err
		}
		config, err := clientcmd.RESTConfigFromKubeConfig([]byte(data.Spec.Kubeconfig))
		if err != nil {
			return nil, err
		}
		t = &Token{
			Name:             clusterID,
			RestClientConfig: config,
		}
		tokenMap.Store(clusterID, t)
	} else {
		t = cacheToken.(*Token)
	}
	return
}

// Token comment
type Token struct {
	Name             string
	RestClientConfig *restclient.Config
	proxy            http.Handler
}

// Do comment
func (t *Token) Do(res http.ResponseWriter, req *http.Request, path string) {
	req.URL.Path = path
	req.Header.Del("Authorization")
	if t.proxy == nil {
		//t.initProxy()
		t.proxy, _ = proxy.NewProxyHandler("/", nil, t.RestClientConfig, 30*time.Second)
	}
	t.proxy.ServeHTTP(res, req)
}
